
(function() {
	var _symbol = d3.svg.symbol(),
		_line = d3.svg.line();

	d3.superformula = function() {
		var type = _symbol.type(),
			size = _symbol.size(),
			segments = size,
			params = {};

		function superformula(d, i) {
			var n, p = _superformulaTypes[type.call(this, d, i)];
			for (n in params) p[n] = params[n].call(this, d, i);
			return _superformulaPath(p, segments.call(this, d, i), Math.sqrt(size.call(this, d, i)));
		}

		superformula.type = function(x) {
			if (!arguments.length) return type;
			type = d3.functor(x);
			return superformula;
		};

		superformula.param = function(name, value) {
			if (arguments.length < 2) return params[name];
			params[name] = d3.functor(value);
			return superformula;
		};

		// size of superformula in square pixels
		superformula.size = function(x) {
			if (!arguments.length) return size;
			size = d3.functor(x);
			return superformula;
		};

		// number of discrete line segments
		superformula.segments = function(x) {
			if (!arguments.length) return segments;
			segments = d3.functor(x);
			return superformula;
		};

		return superformula;
	};

	function _superformulaPath(params, n, diameter) {
		var i = -1,
			dt = 2 * Math.PI / n,
			t,
			r = 0,
			x,
			y,
			points = [];

		while (++i < n) {
			t = params.m * (i * dt - Math.PI) / 4;
			t = Math.pow(Math.abs(Math.pow(Math.abs(Math.cos(t) / params.a), params.n2)
				+ Math.pow(Math.abs(Math.sin(t) / params.b), params.n3)), -1 / params.n1);
			if (t > r) r = t;
			points.push(t);
		}

		r = diameter * Math.SQRT1_2 / r;
		i = -1; while (++i < n) {
			x = (t = points[i] * r) * Math.cos(i * dt);
			y = t * Math.sin(i * dt);
			points[i] = [Math.abs(x) < 1e-6 ? 0 : x, Math.abs(y) < 1e-6 ? 0 : y];
		}

		return _line(points) + "Z";
	}

	var _superformulaTypes = {
		asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
		bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1},
		butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1},
		circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1},
		clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
		cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1},
		cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1},
		diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1},
		drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1},
		ellipse: {m: 4, n1: 2, n2: 2, n3: 2, a: 9, b: 6},
		gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1},
		heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18},
		heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
		hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
		malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1},
		pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1},
		rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1},
		roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1},
		square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1},
		star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1},
		triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1}
	};

	d3.superformulaTypes = d3.keys(_superformulaTypes);
})();